package com.kuizii.generator.util.generator;

import com.google.common.base.CaseFormat;
import com.kuizii.generator.domain.entity.ColumnEntity;
import com.kuizii.generator.domain.entity.TableEntity;
import com.kuizii.generator.util.DateUtils;
import com.kuizii.generator.util.RRException;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static com.kuizii.generator.util.generator.GeneratorApiUtils.isOracle;

/**
 * <p>
 * 自动生成biz模块 //TODO 还没生成测试代码
 * </p>
 *
 * @author nuanyang
 * Email
 * created by 2018/6/20
 */
public class GeneratorCoreUtils {
    private static final Logger logger = LoggerFactory.getLogger(com.kuizii.generator.util.generator.GeneratorConfig.class);


    private static List<String> getOnceTemplates() {
        List<String> templates = new ArrayList<>();
        templates.add("templates/core/pom/corepom.xml.vm");
        templates.add("templates/core/constant/constant-package-info.java.vm");
        templates.add("templates/core/config/config-package-info.java.vm");
        templates.add("templates/starter/resources/application.yml.vm");
        templates.add("templates/starter/resources/application-dev.yml.vm");
        templates.add("templates/starter/resources/application-prod.yml.vm");
        templates.add("templates/starter/resources/application-test.yml.vm");
        templates.add("templates/starter/resources/bootstrap.yml.vm");
        templates.add("templates/banner.txt");
        templates.add("templates/starter/SpringBootApplication.java.vm");
//        if (GeneratorConfig.getConfig().getString("rpc-type").equals("dubbo")) {
//            templates.add("templates/core/config/DubboDocConfig.java.vm");
//        }


        return templates;
    }

    /**
     * 获取模板
     *
     * @return
     */
    private static List<String> getTemplates() {
        List<String> templates = new ArrayList<>();
        templates.add("templates/core/mapper/Mapper.java.vm");
        templates.add("templates/core/mapper/Mapper.xml.vm");
        templates.add("templates/core/entity/Entity.java.vm");
//        templates.add("templates/core/data/service/DataService.java.vm");
//        templates.add("templates/core/data/service/impl/DataServiceImpl.java.vm");
        templates.add("templates/core/service/Service.java.vm");
        templates.add("templates/core/controller/Controller.java.vm");

        return templates;
    }

    private static List<String> getNullFile() {
        List<String> templates = new ArrayList<>();
        templates.add("convert" + File.separator);
        templates.add("domain" + File.separator + "entity" + File.separator);
        templates.add("data" + File.separator + "service" + File.separator);
        templates.add("data" + File.separator + "service" + File.separator + "impl" + File.separator);
        templates.add("mapper" + File.separator);
        templates.add("service" + File.separator);
        templates.add("controller" + File.separator);
        return templates;
    }


    /**
     * 生成空文件夹
     *
     * @param zip
     */
    public static void generatorNullFile(ZipOutputStream zip) {
        try {
            for (String fileName :
                    getNullFile()) {
                //添加到zip
                zip.putNextEntry(new ZipEntry(GeneratorConfig.getProjectJavaRoute(GeneratorConfig.MODULE_CORE) + fileName));
                zip.closeEntry();
            }
        } catch (IOException e) {
            throw new RRException("渲染模板失败", e);
        }

    }

    /**
     * 生成非数据库表代码
     *
     * @param zip
     */
    public static void generatorOnceCode(ZipOutputStream zip) {
        generatorNullFile(zip);
        Configuration config = GeneratorConfig.getConfig();
        //设置velocity资源加载器
        Properties prop = new Properties();
        prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        Velocity.init(prop);
        //封装模板数据
        Map<String, Object> map = new HashMap<>();
        map.put("springBootName", WordUtils.capitalize(GeneratorConfig.getModuleName().replace("-", "_")) +
                WordUtils.capitalize(GeneratorConfig.MODULE_CORE) +
                WordUtils.capitalize(GeneratorConfig.MODULE_STARTER));
        map.put("mainPath", GeneratorConfig.getProjectPackage(GeneratorConfig.MODULE_API));
        map.put("moduleName", GeneratorConfig.getModuleName().replace("-", "."));
        map.put("author", config.getString("author"));
        map.put("email", config.getString("email"));
        map.put("datetime", DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
        map.put("artifactId", GeneratorConfig.getProjectNameNotSlash(GeneratorConfig.MODULE_CORE));
        map.put("description", config.getString("description"));
        map.put("version", config.getString("version"));
        map.put("moduleType", GeneratorConfig.MODULE_CORE);
        map.put("package", config.getString("package"));
        map.put("artifactIdParent", GeneratorConfig.getProjectNameNotSlash(GeneratorConfig.MODULE_API));
        map.put("parentArtifactId", GeneratorConfig.getModuleParent() + GeneratorConfig.getModuleName());
        map.put("sql-type", config.getString("sql-type"));
        map.put("rpc-type", config.getString("rpc-type"));
        map.put("server-port", config.getString("server-port"));
        map.put("context-path", config.getString("context-path"));
        map.put("sql-url", config.getString("sql-url"));
        map.put("sql-port", config.getString("sql-port"));
        map.put("sql-username", config.getString("sql-username"));
        map.put("sql-password", config.getString("sql-password"));
        map.put("sql-db", config.getString("sql-db"));
        map.put("basePackages", config.getString("package") + "." + GeneratorConfig.getModuleName().replace("-", ".") + "." + GeneratorConfig.MODULE_CORE);
        map.put("serverStarterPort", config.getString("serverStarterPort"));
        map.put("applicationName", GeneratorConfig.getProjectName());
        map.put("moduleBizType", GeneratorConfig.MODULE_CORE);
        map.put("coreArtifactId", GeneratorConfig.getModuleParent() + GeneratorConfig.getModulePrefix() + GeneratorConfig.MODULE_CORE);


        VelocityContext context = new VelocityContext(map);
        List<String> templates = getOnceTemplates();
        for (String template : templates) {
            //渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, "UTF-8");
            tpl.merge(context, sw);
            try {
                //添加到zip
                zip.putNextEntry(new ZipEntry(Objects.requireNonNull(getOnceFileName(template))));
                IOUtils.write(sw.toString(), zip, "UTF-8");
                IOUtils.closeQuietly(sw);
                zip.closeEntry();
            } catch (IOException e) {
                e.printStackTrace();
                throw new RRException("渲染模板失败", e);
            }
        }
    }

    /**
     * 自动生成代码
     *
     * @param table
     * @param columns
     * @param zip
     */
    public static void generatorCode(Map<String, String> table,
                                     List<Map<String, String>> columns, ZipOutputStream zip) {
        Configuration config = GeneratorConfig.getConfig();
        boolean hasBigDecimal = false;
        boolean hasDate = false;
        //表信息
        TableEntity tableEntity = new TableEntity();
        tableEntity.setTableName(table.get("tableName"));
        tableEntity.setComments(table.get("tableComment"));

        //表名转换成Java类名
        String className = tableToJava(tableEntity.getTableName(), config.getString("tablePrefix"));
        if (isOracle()) {
            className = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, className);
        } else {
            className = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, className);
        }
        tableEntity.setClassName(className);
        tableEntity.setClassname(StringUtils.uncapitalize(className));

        //列表信息
        List<ColumnEntity> columsList = new ArrayList<>();
        for (Map<String, String> column : columns) {
            //过滤 create_time & update_time 不自动生成
            if (!"create_time".equals(column.get("columnName")) && !"update_time".equals(column.get("columnName"))) {
                ColumnEntity columnEntity = new ColumnEntity();
                columnEntity.setColumnName(column.get("columnName"));
                columnEntity.setDataType(column.get("dataType"));
                columnEntity.setComments(column.get("columnComment") == null ? "" : column.get("columnComment"));
                columnEntity.setExtra(column.get("extra"));

                //列名转换成Java属性名
                String attrName = columnToJava(columnEntity.getColumnName()).toUpperCase(Locale.ROOT);
                attrName = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, columnEntity.getColumnName());
                columnEntity.setAttrName(attrName);
                columnEntity.setAttrname(StringUtils.uncapitalize(attrName));

                //列的数据类型，转换成Java类型
                String attrType = config.getString(columnEntity.getDataType().toLowerCase(Locale.ROOT), "unknowType");
                columnEntity.setAttrType(attrType);
                if (!hasBigDecimal && "BigDecimal".equals(attrType)) {
                    hasBigDecimal = true;
                }
                if (!hasDate && "Date".equals(attrType)) {
                    hasDate = true;
                }

                //是否主键
                if ("PRI".equalsIgnoreCase(column.get("columnKey")) && tableEntity.getPk() == null) {
                    tableEntity.setPk(columnEntity);
                }
                columsList.add(columnEntity);
            }
        }
        tableEntity.setColumns(columsList);

        //没主键，则第一个字段为主键
        if (tableEntity.getPk() == null) {
            tableEntity.setPk(tableEntity.getColumns().get(0));
        }
        //设置velocity资源加载器
        Properties prop = new Properties();
        prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        Velocity.init(prop);
        //封装模板数据
        Map<String, Object> map = new HashMap<>();
        map.put("tableName", tableEntity.getTableName());
        map.put("comments", tableEntity.getComments() == null ? "" : tableEntity.getComments());
        map.put("pk", tableEntity.getPk());
        map.put("className", className);
        map.put("routeClassName", className);
        map.put("pathName", className);
        map.put("lowerClassName", WordUtils.uncapitalize(className));
        map.put("columns", tableEntity.getColumns() == null ? "" : tableEntity.getColumns());
        map.put("hasBigDecimal", hasBigDecimal);
        map.put("hasDate", hasDate);
        map.put("mainPath", GeneratorConfig.getProjectPackage(GeneratorConfig.MODULE_API));
        map.put("moduleName", GeneratorConfig.getModuleName().replace("-", "."));
        map.put("author", config.getString("author"));
        map.put("email", config.getString("email"));
        map.put("datetime", DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
        map.put("artifactId", GeneratorConfig.getProjectNameNotSlash(GeneratorConfig.MODULE_API));
        map.put("description", config.getString("description"));
        map.put("version", config.getString("version"));
        map.put("moduleType", GeneratorConfig.MODULE_CORE);
        map.put("package", config.getString("package"));
        map.put("rpc-type", config.getString("rpc-type"));
        VelocityContext context = new VelocityContext(map);
        List<String> templates = getTemplates();
        for (String template : templates) {
            //渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, "UTF-8");
            tpl.merge(context, sw);
            try {
                //添加到zip
                zip.putNextEntry(new ZipEntry(Objects.requireNonNull(getFileName(template, className))));
                IOUtils.write(sw.toString(), zip, "UTF-8");
                IOUtils.closeQuietly(sw);
                zip.closeEntry();
            } catch (IOException e) {
                e.printStackTrace();
                throw new RRException("渲染模板失败，表名：" + tableEntity.getTableName(), e);
            }
        }


    }

    /**
     * 获取模板对应名称
     *
     * @param template
     * @return
     */
    private static String getOnceFileName(String template) {

        if (template.contains("corepom.xml.vm")) {
            return GeneratorConfig.getPomRoute(GeneratorConfig.MODULE_CORE) + "pom.xml";
        }
        if (template.contains("convert-package-info.java.vm")) {
            return GeneratorConfig.getProjectJavaRoute(GeneratorConfig.MODULE_CORE) + "convert"
                    + File.separator + "DemoConverter.java";
        }

        if (template.contains("constant-package-info.java.vm")) {
            return GeneratorConfig.getProjectJavaRoute(GeneratorConfig.MODULE_CORE) + "constant" + File.separator + "DemoConstants.java";
        }
        if (template.contains("config-package-info.java.vm")) {
            return GeneratorConfig.getProjectJavaRoute(GeneratorConfig.MODULE_CORE) + "config" + File.separator + "DemoConfig.java";
        }
        if (template.contains("SpringBootApplication.java.vm")) {
            return GeneratorConfig.getSpringBootRoute(GeneratorConfig.MODULE_CORE) +
                    WordUtils.capitalize(GeneratorConfig.getModuleName().replace("-", "_")) +
                    WordUtils.capitalize(GeneratorConfig.MODULE_CORE) + WordUtils.capitalize(GeneratorConfig.MODULE_STARTER) + "Application.java";
        }
        if (template.contains("application.yml.vm")) {
            return GeneratorConfig.getProjectResourcesRoute(GeneratorConfig.MODULE_CORE) + "application.yml";
        }
        if (template.contains("application-dev.yml.vm")) {
            return GeneratorConfig.getProjectResourcesRoute(GeneratorConfig.MODULE_CORE) + "application-dev.yml";
        }
        if (template.contains("application-prod.yml.vm")) {
            return GeneratorConfig.getProjectResourcesRoute(GeneratorConfig.MODULE_CORE) + "application-prod.yml";
        }
        if (template.contains("application-test.yml.vm")) {
            return GeneratorConfig.getProjectResourcesRoute(GeneratorConfig.MODULE_CORE) + "application-test.yml";
        }
        if (template.contains("bootstrap.yml.vm")) {
            return GeneratorConfig.getProjectResourcesRoute(GeneratorConfig.MODULE_CORE) + "bootstrap.yml";
        }

        if (template.contains("starterpom.xml.vm")) {
            return GeneratorConfig.getProjectName(GeneratorConfig.MODULE_CORE) + "pom.xml";
        }

        if (template.contains("banner.txt")) {
            return GeneratorConfig.getProjectResourcesRoute(GeneratorConfig.MODULE_CORE) + "banner.txt";
        }
        if (template.contains("DubboDocConfig.java.vm")) {
            return GeneratorConfig.getProjectJavaRoute(GeneratorConfig.MODULE_CORE) + "config" + File.separator + "DubboDocConfig.java";
        }
        return null;
    }


    /**
     * 获取模板对应名称
     *
     * @param template
     * @param className
     * @return
     */
    private static String getFileName(String template, String className) {
        if (template.contains("Entity.java.vm")) {
            return GeneratorConfig.getDomainEntity(GeneratorConfig.MODULE_CORE) + className + "Entity.java";
        }

        if (template.contains("DataService.java.vm")) {
            return GeneratorConfig.getDataServiceRoute(GeneratorConfig.MODULE_CORE) + className + "DataService.java";
        }

        if (template.contains("DataServiceImpl.java.vm")) {
            return GeneratorConfig.getDataSeiviceImplRoute(GeneratorConfig.MODULE_CORE) + className + "DataServiceImpl.java";
        }

        if (template.contains("Mapper.java.vm")) {
            return GeneratorConfig.getMapperRoute(GeneratorConfig.MODULE_CORE) + className + "Mapper.java";
        }

        if (template.contains("Mapper.xml.vm")) {
            return GeneratorConfig.getMapperXMLRoute(GeneratorConfig.MODULE_CORE) + className + "Mapper.xml";
        }

        if (template.contains("Service.java.vm")) {
            return GeneratorConfig.getService(GeneratorConfig.MODULE_CORE) + className + "ServiceImpl.java";
        }
        if (template.contains("Controller.java.vm")) {
            return GeneratorConfig.getController(GeneratorConfig.MODULE_CORE) +  className + "Controller.java";
        }
        return null;
    }


    /**
     * 列名转换成Java属性名
     */
    private static String columnToJava(String columnName) {
        Configuration config = GeneratorConfig.getConfig();
        if ("mysql".equals(config.getString("sql-type"))) {
            if (columnName.length() > 3) {
                //如果数据库是is开头的，去除is，转成成java类型
                if (columnName.startsWith("is_")) {
                    columnName = columnName.substring(3);
                }
            }
            if (columnName.contains("_")) {
                return WordUtils.capitalizeFully(columnName, new char[]{'_'}).replace("_", "");
            }
            return columnName;
        } else {
            if (columnName.toLowerCase().startsWith("is")) {
                columnName = columnName.substring(2);
            }
            return columnName;
        }

    }

    /**
     * 表名转换成Java类名
     */
    private static String tableToJava(String tableName, String tablePrefix) {
        if (StringUtils.isNotBlank(tablePrefix) && tablePrefix.indexOf(tablePrefix) == 0) {
            tableName = tableName.replaceFirst(tablePrefix, "");
        }
        tableName = columnToJava(tableName);
        //表名首字母大写
        if (tableName.length() > 1) {
            tableName = String.format("%s%s",
                    StringUtils.upperCase(tableName.substring(0, 1)),
                    tableName.substring(1));
        }
        return tableName;
    }
}
